RCrawler101-201605 (Week2)

Leo Lu

(This material is modified from Mansun Kuo’s work)



RCrawler101-201605 (Week2)

2016-05-15

Leo Lu

How to use this slides

Outline

Install: Chrome Extension

Recap Observation Skills and HTTP request for Connection

R Packages Required

Pipeline Coding

Crawler’s toolkits in R

misc

Install Packages

(if you haven’t installed those packages yet)

## === install required packages ===
pkg_list <- c("magrittr", "httr", "rvest", "stringr", "data.table",
              "jsonlite", "RSQLite")
pkg_new <- pkg_list[!(pkg_list %in% installed.packages()[,"Package"])]
if(length(pkg_new)) install.packages(pkg_new)
rm(pkg_new, pkg_list)

Let’s Rock with R!

Hello RStudio

rstudio

RStudio Settings

Must-known keyboard shortcuts

All RStudio keyboard shortcuts

Description Windows & Linux Mac
Attempt completion / Indent Tab Tab
Run current line/selection Ctrl+Enter +↩︎
Comment/uncomment current line/selection Ctrl+Shift+C ++C
Reindent lines Ctrl+I +I
Insert pipe operator Ctrl+Shift+M ++M

R recap

How to get help

Working Environment

Check your working directory everytime you start to work!

Using getwd/setwd to get/set your working directory.

wd = getwd()
setwd("resources/img")
getwd()
setwd(wd)
getwd()

RStudio will set working directory automatically when opening new files.

If you using Projects, RStudio will change working directory for you automatically.

Basic Data Structure

Vector, Matrix, Array, List and Data frame are the most basic data structure in R. These data structures can be mapped into a table according to:

Homogeneous Heterogeneous
1d Atomic vector List
2d Matrix Data frame
nd Array

(Atomic) Vector

v1 <- c(1:10)
v1
#>  [1]  1  2  3  4  5  6  7  8  9 10
is.vector(v1)
#> [1] TRUE
length(v1)
#> [1] 10
s1 <- 2
s1
#> [1] 2
is.vector(s1)
#> [1] TRUE
length(s1)
#> [1] 1

List

Lists are also vectors, but not atomic vectors. Lists are generic vectors, with (naturally) different semantics.

Elements in a list can be any kinds type and its length is arbitrary.

Function str can help you investigate the structure of a nested list.

li <- list(a = 1:10, 
           b = c("apple", "banana"))
str(li)
#> List of 2
#>  $ a: int [1:10] 1 2 3 4 5 6 7 8 9 10
#>  $ b: chr [1:2] "apple" "banana"
li2 <- list(li = li, 
            c = matrix(1:4, nrow = 2))
str(li2)
#> List of 2
#>  $ li:List of 2
#>   ..$ a: int [1:10] 1 2 3 4 5 6 7 8 9 10
#>   ..$ b: chr [1:2] "apple" "banana"
#>  $ c : int [1:2, 1:2] 1 2 3 4

Visualising lists

x1 <- list(c(1, 2), c(3, 4))
x2 <- list(list(1, 2), list(3, 4))
x3 <- list(1, list(2, list(3)))

Lists Subsetting

a <- list(a = 1:3, b = "a string", c = pi, d = list(-1, -5))
str(a[1:2])
str(a[4])
y <- list("a", 1L, 1.5, TRUE)
str(y[[1]])
str(y[[4]])
a$a
a[["b"]]

Lists Subsetting (Pepper Shaker)

Data Frame

Data frame is a 2-dimension data structure to deal with a table-like heterogeneous data.

df <- data.frame(gender = c("male", "female", "female", "male"),
                 age = c(33, 18, 24, 26))
## Add new column in a data frame
df$city <- c("Taipei", "Taipei", "Hsinchu", "Taichung")
df
#>   gender age     city
#> 1   male  33   Taipei
#> 2 female  18   Taipei
#> 3 female  24  Hsinchu
#> 4   male  26 Taichung
str(df)
#> 'data.frame':    4 obs. of  3 variables:
#>  $ gender: Factor w/ 2 levels "female","male": 2 1 1 2
#>  $ age   : num  33 18 24 26
#>  $ city  : chr  "Taipei" "Taipei" "Hsinchu" "Taichung"

Recap: data structure

All data structures above are objects. They apply different methods and saved as different type internally.

object type class
c(1, 2.5, 3) double numeric
c(“male”, “female”, “female”, “male”) character character
factor(c(“male”, “female”, “female”, “male”)) integer factor
matrix(1:9, nrow = 3) integer matrix
list(a = 1:10, b = c(“apple”, “banana”)) list list
data.frame(a = 1, b = “z”) list data.frame

Function

To understand computations in R, two slogans are helpful:

John Chambers


`+`
#> function (e1, e2)  .Primitive("+")
`<-`
#> .Primitive("<-")
`[`
#> .Primitive("[")
`c`
#> function (..., recursive = FALSE)  .Primitive("c")

Function in R

A typical function in R may look like:

f <- function(par1, par2, ...) {
    # Some magic happened
    return(sth)    # return something
}

Control Flow

If

The basc structure of conditional execution in R is:

if (an expression returns TRUE or FALSE) {
    # do something
} else if (another expression returns TRUE or FALSE) {
    # do something
} else {
    # do something
}

for

Iterate items in R.

# iterate a character vector
for (i in c("a", "b")) {
    print(i)
}
#> [1] "a"
#> [1] "b"
# nested loop
m <- matrix(numeric(), nrow = 2, ncol = 2)
for (i in 1:nrow(m)) {
    for (j in 1:ncol(m)) {
        m[i, j] <- i * j
    }
}
m
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    2    4

tryCatch

tryCatch({
  result <- expr
  # If you want to use more than one 
  # R expression in the "try" part then you'll have to 
  # use curly brackets.
  # 'tryCatch()' will return the last evaluated expression 
  # in case the "try" part was completed successfully
}, warning = function(w) {
  message("Here's the original warning message:")
  message(w)
  # Choose a return value in case of warning
  return(NULL)
}, error = function(e) {
  message("Here's the original error message:")
  message(e)
  # Choose a return value in case of error
  return(NA)
},
}, finally {
  message("Some other message at the end")
  # finally:
  # Here goes everything that should be executed at the end,
  # regardless of success or error.
  # If you want more than one expression to be executed, then you 
  # need to wrap them in curly brackets ({...}); otherwise you could
  # just have written 'finally=<expression>' 
})

https://stackoverflow.com/questions/12193779/how-to-write-trycatch-in-r/12195574#12195574

magrittr

magrittr logo

What is magrittr

Pipe argument to right-hand side with %>%


Example

library(magrittr)
iris %>% head()
"Ceci n'est pas une pipe" %>% gsub("une", "un", .)

Workflow of Crawler Design

Architecture of Crawlers

Workflow

  1. 找到資料頁,想像資料要長什麼樣子,設想產出的資料格式(schema)
  2. 觀察網頁內容,找到資料所在的request/response,再一層層往上解析,套上判斷式及迴圈, 完成爬蟲的自動化。
  3. 解析取得的資料。

Crawler’s toolkits in R



Web Connector in R

HTTP request

A valid HTTP request includes four things:

Web Connector in R

Connection: GET Method

Use GET() to request data from a specific resource

起手式

## Not Run
library(httr)
library(rvest)
res <- GET(
  url = "http://httpbin.org/get",
  add_headers(a = 1, b = 2),
  set_cookies(c = 1, d = 2),
  query = list(q="hihi")
)
content(res, as = "text", encoding = "UTF-8")
content(res, as = "parsed", encoding = "UTF-8")

一個例子學會第一隻爬蟲

library(magrittr)
library(httr)
library(rvest)
#> Loading required package: xml2
url <- "https://www.ptt.cc/bbs/Gossiping/index.html"
doc <- GET(url, 
           set_cookies('over18'='1')) %>%  # over18 cookie
  content(as = "text", encoding = "UTF-8") %>% 
  read_html

doc %>% 
    html_nodes(xpath = '//*[@id="action-bar-container"]/div/div[2]/a[2]') %>% 
    html_attr("href")
#> [1] "/bbs/Gossiping/index14934.html"

httr

Parsing Response Content

httr basics

library(httr)
res <- GET(
  url = "http://httpbin.org/get",
  add_headers(a = 1, b = 2),
  set_cookies(c = 1, d = 2),
  query = list(q="hihi")
)

status_code(res)

headers(res)

content(res, type = "text", encoding = "UTF-8")

Set header

Sometime you may need to provide appropriate HTTP header fields with add_headers() to make a request.

Cookies

Connection: POST method

起手式

## Not Run
library(httr)
library(rvest)


res1 <- POST(url = "http://httpbin.org/post", 
              add_headers(a = 1, b = 2),
              set_cookies(c = 1, d = 2),
              body = "A simple text string")

res2 <- POST(url = "http://httpbin.org/post",
             add_headers(a = 1, b = 2),
             set_cookies(c = 1, d = 2),
             body = list(x = "A simple text string", 
                         y = "hihi"))

identical(res1, res2)
content(res2, as = "text", encoding = "UTF-8")
content(res2, as = "parsed", encoding = "UTF-8")

一個例子學會第二隻爬蟲: Guestbook

Guestbook

Exercise: Guestbook

Try to post a message in App Engine GuestBook

The response status code

res <- GET("http://httpbin.org/get")

# Get an informative description:
http_status(res)
#> $category
#> [1] "Success"
#> 
#> $reason
#> [1] "OK"
#> 
#> $message
#> [1] "Success: (200) OK"

# Or just access the raw code:
res$status_code
#> [1] 200

# highly recommend using one of these functions whenever you're using httr inside a function to make sure you find out about errors as soon as possible.
warn_for_status(res)
stop_for_status(res)

The response Body

3 ways to access the body of the request:

res <- GET("http://httpbin.org/get")
content(res, "text", encoding = "UTF-8")  # accesses the body as a character vector
#> [1] "{\n  \"args\": {}, \n  \"headers\": {\n    \"Accept\": \"application/json, text/xml, application/xml, */*\", \n    \"Accept-Encoding\": \"gzip, deflate\", \n    \"Host\": \"httpbin.org\", \n    \"User-Agent\": \"libcurl/7.43.0 r-curl/0.9.7 httr/1.1.0\"\n  }, \n  \"origin\": \"123.193.211.51\", \n  \"url\": \"http://httpbin.org/get\"\n}\n"
(bin <- content(res, "raw"))  # raw (binary) vector
#>   [1] 7b 0a 20 20 22 61 72 67 73 22 3a 20 7b 7d 2c 20 0a 20 20 22 68 65 61
#>  [24] 64 65 72 73 22 3a 20 7b 0a 20 20 20 20 22 41 63 63 65 70 74 22 3a 20
#>  [47] 22 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6a 73 6f 6e 2c 20 74 65 78 74
#>  [70] 2f 78 6d 6c 2c 20 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 6d 6c 2c 20
#>  [93] 2a 2f 2a 22 2c 20 0a 20 20 20 20 22 41 63 63 65 70 74 2d 45 6e 63 6f
#> [116] 64 69 6e 67 22 3a 20 22 67 7a 69 70 2c 20 64 65 66 6c 61 74 65 22 2c
#> [139] 20 0a 20 20 20 20 22 48 6f 73 74 22 3a 20 22 68 74 74 70 62 69 6e 2e
#> [162] 6f 72 67 22 2c 20 0a 20 20 20 20 22 55 73 65 72 2d 41 67 65 6e 74 22
#> [185] 3a 20 22 6c 69 62 63 75 72 6c 2f 37 2e 34 33 2e 30 20 72 2d 63 75 72
#> [208] 6c 2f 30 2e 39 2e 37 20 68 74 74 72 2f 31 2e 31 2e 30 22 0a 20 20 7d
#> [231] 2c 20 0a 20 20 22 6f 72 69 67 69 6e 22 3a 20 22 31 32 33 2e 31 39 33
#> [254] 2e 32 31 31 2e 35 31 22 2c 20 0a 20 20 22 75 72 6c 22 3a 20 22 68 74
#> [277] 74 70 3a 2f 2f 68 74 74 70 62 69 6e 2e 6f 72 67 2f 67 65 74 22 0a 7d
#> [300] 0a
# writeBin(bin, "myfile.txt") # the highest fidelity way of saving files to disk
content(res, "parsed")
#> $args
#> named list()
#> 
#> $headers
#> $headers$Accept
#> [1] "application/json, text/xml, application/xml, */*"
#> 
#> $headers$`Accept-Encoding`
#> [1] "gzip, deflate"
#> 
#> $headers$Host
#> [1] "httpbin.org"
#> 
#> $headers$`User-Agent`
#> [1] "libcurl/7.43.0 r-curl/0.9.7 httr/1.1.0"
#> 
#> 
#> $origin
#> [1] "123.193.211.51"
#> 
#> $url
#> [1] "http://httpbin.org/get"

The secret of URL

URL?par1=val1&par2=val2

Query String

You can assign query parameter with query()

res1 <- GET(
  "http://ecshweb.pchome.com.tw/search/v3.3/all/results?q=sony&page=1&sort=rnk/dc"
)

url2 <- "http://ecshweb.pchome.com.tw/search/v3.3/all/results"
res2 <- GET(url2,
           query = list(q="sony", page="1", sort="rnk/dc"))
identical(res1$content, res2$content)
#> [1] TRUE

Url Encoding in R

URLencode(" ")
#> [1] "%20"
greeting = "你好嗎我很好"
greeting_enc = URLencode(greeting)
greeting_enc
#> [1] "%E4%BD%A0%E5%A5%BD%E5%97%8E%E6%88%91%E5%BE%88%E5%A5%BD"
URLdecode(greeting_enc)
#> [1] "你好嗎我很好"

Concatenate strings / String formatting

paste0("hihi", greeting)
#> [1] "hihi你好嗎我很好"
paste0("hihi", greeting, 1:3)
#> [1] "hihi你好嗎我很好1" "hihi你好嗎我很好2" "hihi你好嗎我很好3"
paste("hihi", greeting, 1:3, sep = " ", collapse = ",")
#> [1] "hihi 你好嗎我很好 1,hihi 你好嗎我很好 2,hihi 你好嗎我很好 3"
sprintf("%s,%s嗎?", "hihi", greeting)
#> [1] "hihi,你好嗎我很好嗎?"



Parsing Data

Parsing Response Content

What to Parse

結構化資料

非結構化資料

rvest

A web scraper designed to work with magrittr.

Parsing Response Content: Text Type

Use with rvest

httr::GET(url) %>% httr::content(as="text") %>% rvest::read_html()

Beware of System Encoding

Encoding

?Encoding ?iconv

## check out your system locale
Sys.getlocale()
#> [1] "en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8"
aa <- "你好嗎"
Encoding(aa)
#> [1] "UTF-8"
charToRaw(aa)
#> [1] e4 bd a0 e5 a5 bd e5 97 8e

(aa_big5 <- iconv(aa, from = "UTF-8", to = "Big5"))
#> [1] "\xa7A\xa6n\xb6\xdc"
Encoding(aa_big5)
#> [1] "unknown"
harToRaw(aa_big5)
#> [1] a7 41 a6 6e b6 dc
# On Windows
Sys.getlocale()
aa <- "你好嗎"
Encoding(aa)
aa_utf8 <- iconv(aa, from = "Big5", to = "UTF-8")
Encoding(aa)

Extract elements from HTML document

Tree Structure of HTML (Document Object Model, DOM)

<a href = "www.meetup.com/Taiwan-R">
  Taiwan R User Group Website
</a>

A simple HTML document

demo_page

library(magrittr)
doc = readLines("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html") %>%
    paste(collapse = "\n")
cat(doc)
#> <!DOCTYPE HTML>
#> <html>
#>     <body>
#>         <div id='title' class='character'>
#>             <a class="link" href="http://data-sci.info/r-crawler-101/">data-sci.info</a>
#>         </div>
#>         <div id='summary' class='character'>
#>             <span class='title'>Number:</span>
#>             <span class='number'>2</span>
#>         </div>
#>         <div id='table1' class='info'>
#>             <table style="border-collapse: collapse; border: 1px solid black;">
#>                 <tr>
#>                     <th>Name</th>
#>                     <th>Gender</th>
#>                     <th>Age</th>
#>                 </tr>
#>                 <tr>
#>                     <td>Alice</td>
#>                     <td>Female</td>
#>                     <td>24</td>
#>                 </tr>
#>                 <tr>
#>                     <td>Jane</td>
#>                     <td>Female</td>
#>                     <td>26</td>
#>                 </tr>
#>             </table>
#>         </div>
#>     </body>
#> </html>

Create HTML document object

library(rvest)
doc <- read_html("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html")
doc
#> {xml_document}
#> <html>
#> [1] <body>\n        <div id="title" class="character">\n            <a c ...
class(doc)
#> [1] "xml_document" "xml_node"

Extract with css

CSS practice
http://flukeout.github.io/

doc <- read_html("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html")
doc %>% 
    html_nodes(css = ".character") %>% 
    html_text
#> [1] "\n            data-sci.info\n        "         
#> [2] "\n            Number:\n            2\n        "
doc %>% 
    html_nodes(css = "#title > .link") %>% 
    html_text
#> [1] "data-sci.info"

Extract with xpath

doc <- read_html("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html")
doc %>% 
    html_nodes(xpath = "//*[@class='character']") %>% 
    html_text
#> [1] "\n            data-sci.info\n        "         
#> [2] "\n            Number:\n            2\n        "
doc %>% 
    html_nodes(xpath = "//div[@id='title']/a") %>% 
    html_text
#> [1] "data-sci.info"

Extract name of tag

node = doc %>% 
    html_nodes(css = "#summary") %>% 
    html_name
node
#> [1] "div"

Example: PTT

use package xmlview to parse XML content and extract elements.

Extract table

(rvest::html_table is temperarily not working with unicode data.)

students <- read_html("http://leoluyi.github.io/RCrawler101_201605_Week2/resources/data/demo.html") %>% 
    html_nodes(xpath = "//table") %>%
    html_table()
students
#> [[1]]
#>    Name Gender Age
#> 1 Alice Female  24
#> 2  Jane Female  26

Extract table

use XML::readHTMLTable()

res_text <- GET("https://zh.wikipedia.org/wiki/%E5%9B%BD%E5%AE%B6%E4%BA%BA%E5%8F%A3%E5%88%97%E8%A1%A8") %>% 
  content("text", encoding = "UTF-8") %>% 
  `Encoding<-`("UTF-8")

# create `HTMLInternalDocument` for following use
doc <- XML::htmlParse(res_text, encoding = "UTF-8")
class(doc)
#> [1] "HTMLInternalDocument" "HTMLInternalDocument" "XMLInternalDocument" 
#> [4] "XMLAbstractDocument"
# Parse tables with XML::readHTMLTable()
tables <- XML::readHTMLTable(doc)

# str(tables) # take a look 
# View(tables[[2]]) # take a look 
排名 國家/地區 人口 資料日期 佔世界比例 來源
世界 7,323,900,000 2016年5月1日 100% 美國人口普查局-世界人口時鐘
1 中华人民共和国[註 1] 1,376,400,000 2016年5月1日 18.8% 官方人口時鐘
2 印度 1,325,500,000 2016年5月1日 18.1% 人口時鐘预测
3 美國 323,530,000 2016年5月1日 4.42% 官方人口時鐘
4 印尼 260,460,000 2016年5月1日 3.56% 人口时钟预测
5 巴西 205,870,000 2016年5月1日 2.81% 官方人口時鐘
6 巴基斯坦 193,660,000 2016年5月1日 2.64% 官方人口時鐘
7 奈及利亞 186,360,000 2016年5月1日 2.55% 人口时钟预测
8 孟加拉国 162,650,000 2016年5月1日 2.22% 人口时钟预测
9 俄羅斯[註 2] 146,380,000 2016年5月1日 2% 官方人口時鐘
10 墨西哥 128,550,000 2016年5月1日 1.76% 人口时钟预测
11 日本 126,980,000 2016年4月1日 1.73% 日本統計局
12 菲律賓 103,020,000 2016年5月1日 1.41% 官方人口時鐘
13 衣索比亞 101,550,000 2016年5月1日 1.39% 人口时钟预测
14 越南 94,352,000 2016年5月1日 1.29% 人口时钟预测
15 埃及 90,885,000 2016年5月1日 1.24% 官方人口時鐘
16 德國 81,267,000 2016年5月1日 1.11% 人口时钟预测
17 土耳其 79,884,000 2016年1月1日 1.09% 人口时钟预测
18 刚果(金) 79,346,000 2016年5月1日 1.08% 人口时钟预测
19 伊朗 79,245,000 2016年5月1日 1.08% 官方人口時鐘
20 泰國 68,191,000 2016年5月1日 0.93% 人口时钟预测
21 英國 65,075,000 2016年5月1日 0.89% 人口时钟预测
22 法国[註 3] 64,652,000 2016年5月1日 0.88% 人口时钟预测
23 義大利 59,851,000 2016年5月1日 0.82% 人口时钟预测
24 南非 55,027,000 2016年5月1日 0.75% 人口时钟预测
25 坦桑尼亚 54,907,000 2016年5月1日 0.75% 人口时钟预测
26 緬甸 54,265,000 2016年5月1日 0.74% 人口时钟预测
27 韩国 50,514,000 2016年5月1日 0.69% 人口时钟预测
28 哥伦比亚 48,674,000 2016年5月1日 0.66% 官方人口時鐘
29 肯尼亚 47,096,000 2016年5月1日 0.64% 人口时钟预测
30 西班牙[註 4] 46,061,000 2016年5月1日 0.63% 人口时钟预测
31 阿根廷 43,808,000 2016年5月1日 0.6% 人口时钟预测
32 烏克蘭[註 5] 42,561,000 2016年5月1日 0.58% 人口时钟预测
33 苏丹 40,961,000 2016年5月1日 0.56% 人口时钟预测
34 阿尔及利亚 40,326,000 2016年5月1日 0.55% 人口时钟预测
35 乌干达 40,112,000 2016年5月1日 0.55% 人口时钟预测
36 波蘭 38,626,000 2016年5月1日 0.53% 人口时钟预测
37 伊拉克 37,459,000 2016年5月1日 0.51% 人口时钟预测
38 加拿大 36,267,000 2016年5月1日 0.5% 人口时钟预测
39 摩洛哥[註 6] 34,784,000 2016年5月1日 0.48% 人口时钟预测
40 阿富汗 33,395,000 2016年5月1日 0.46% 人口时钟预测
41 沙烏地阿拉伯 32,196,000 2016年5月1日 0.44% 人口时钟预测
42 秘魯 31,734,000 2016年5月1日 0.43% 人口时钟预测
43 委內瑞拉 31,492,000 2016年5月1日 0.43% 人口时钟预测
44 马来西亚 31,341,000 2016年5月1日 0.43% 官方人口時鐘
45 乌兹别克斯坦 30,285,000 2016年5月1日 0.41% 人口时钟预测
46 尼泊尔 28,800,000 2016年5月1日 0.39% 人口时钟预测
47 莫桑比克 28,647,000 2016年5月1日 0.39% 人口时钟预测
48 加纳 27,980,000 2016年5月1日 0.38% 人口时钟预测
49 葉門 27,435,000 2016年5月1日 0.37% 人口时钟预测
50 安哥拉 25,727,000 2016年5月1日 0.35% 人口时钟预测
51 朝鲜 25,271,000 2016年5月1日 0.35% 人口时钟预测
52 马达加斯加 24,810,000 2016年5月1日 0.34% 人口时钟预测
53 澳大利亚[註 7] 24,070,000 2016年5月1日 0.33% 官方人口時鐘
54 喀麦隆 23,845,000 2016年5月1日 0.33% 人口时钟预测
55 中華民國 23,497,000 2016年3月1日 0.32% 中華民國統計資訊網
56 科特迪瓦 23,162,000 2016年5月1日 0.32% 人口时钟预测
57 斯里蘭卡 20,808,000 2016年5月1日 0.28% 人口时钟预测
58 尼日尔 20,564,000 2016年5月1日 0.28% 人口时钟预测
59 羅馬尼亞 19,371,000 2016年5月1日 0.26% 人口时钟预测
60 布吉納法索 18,560,000 2016年5月1日 0.25% 人口时钟预测
61 智利 18,115,000 2016年5月1日 0.25% 人口时钟预测
62 叙利亚 18,064,000 2016年5月1日 0.25% 人口时钟预测
63 马里 18,044,000 2016年5月1日 0.25% 人口时钟预测
64 哈萨克斯坦 17,867,000 2016年5月1日 0.24% 人口时钟预测
65 馬拉威 17,661,000 2016年5月1日 0.24% 人口时钟预测
66 荷蘭 16,977,000 2016年5月1日 0.23% 官方人口時鐘
67 危地马拉 16,636,000 2016年5月1日 0.23% 人口时钟预测
68 尚比亞 16,628,000 2016年5月1日 0.23% 人口时钟预测
69 厄瓜多尔 16,363,000 2016年5月1日 0.22% 人口时钟预测
70 辛巴威 15,887,000 2016年5月1日 0.22% 人口时钟预测
71 柬埔寨 15,794,000 2016年5月1日 0.22% 人口时钟预测
72 塞内加尔 15,528,000 2016年5月1日 0.21% 人口时钟预测
73 乍得 14,433,000 2016年5月1日 1.97‰ 人口时钟预测
74 几内亚 12,900,000 2016年5月1日 1.76‰ 人口时钟预测
75 南蘇丹 12,798,000 2016年5月1日 1.75‰ 人口时钟预测
76 卢旺达 11,851,000 2016年5月1日 1.62‰ 人口时钟预测
77 布隆迪 11,496,000 2016年5月1日 1.57‰ 人口时钟预测
78 古巴 11,407,000 2016年5月1日 1.56‰ 人口时钟预测
79 比利時 11,365,000 2016年5月1日 1.55‰ 人口时钟预测
80 突尼西亞 11,363,000 2016年5月1日 1.55‰ 人口时钟预测
81 贝宁 11,132,000 2016年5月1日 1.52‰ 人口时钟预测
82 索馬利亞[註 8] 10,995,000 2016年5月1日 1.5‰ 人口时钟预测
83 希臘 10,918,000 2016年5月1日 1.49‰ 人口时钟预测
84 玻利维亚 10,869,000 2016年5月1日 1.48‰ 人口时钟预测
85 海地 10,840,000 2016年5月1日 1.48‰ 人口时钟预测
86 多米尼加 10,643,000 2016年5月1日 1.45‰ 人口时钟预测
87 捷克 10,553,000 2016年5月1日 1.44‰ 人口时钟预测
88 葡萄牙 10,313,000 2016年5月1日 1.41‰ 人口时钟预测
89 阿塞拜疆 9,874,900 2016年5月1日 1.35‰ 人口时钟预测
90 瑞典 9,851,200 2016年5月1日 1.35‰ 人口时钟预测
91 匈牙利 9,829,100 2016年5月1日 1.34‰ 人口时钟预测
92 白俄羅斯 9,499,200 2016年5月1日 1.3‰ 人口时钟预测
93 阿联酋 9,356,500 2016年5月1日 1.28‰ 人口时钟预测
94 塔吉克斯坦 8,617,000 2016年1月1日 1.18‰ 人口时钟预测
95 宏都拉斯 8,552,400 2016年1月1日 1.17‰ 人口时钟预测
96 奥地利 8,441,500 2016年1月1日 1.15‰ 人口时钟预测
97 以色列 8,424,400 2016年1月1日 1.15‰ 人口时钟预测
98 瑞士 8,064,300 2016年1月1日 1.1‰ 人口时钟预测
99 巴布亚新几内亚 7,753,900 2016年1月1日 1.06‰ 人口时钟预测
100 多哥 7,408,300 2016年1月1日 1.01‰ 人口时钟预测
101 香港 7,304,100 2016年1月1日 1‰ 香港政府統計處網站
102 老挝 7,105,200 2016年1月1日 0.97‰ 人口时钟预测
103 塞爾維亞[註 9] 7,090,000 2016年1月1日 0.97‰ 人口时钟预测
104 保加利亚 7,079,800 2016年1月1日 0.97‰ 人口时钟预测
105 巴拉圭 7,037,500 2016年1月1日 0.96‰ 人口时钟预测
106 约旦 6,888,700 2016年1月1日 0.94‰ 人口时钟预测
107 厄立特里亚 6,760,400 2016年1月1日 0.92‰ 人口时钟预测
108 利比亞 6,679,000 2016年1月1日 0.91‰ 人口时钟预测
109 塞拉利昂 6,535,100 2016年1月1日 0.89‰ 人口时钟预测
110 薩爾瓦多 6,377,900 2016年1月1日 0.87‰ 人口时钟预测
111 尼加拉瓜 6,257,200 2016年1月1日 0.85‰ 人口时钟预测
112 吉尔吉斯斯坦 5,934,400 2016年1月1日 0.81‰ 人口时钟预测
113 丹麥 5,648,000 2016年1月1日 0.77‰ 人口时钟预测
114 芬兰[註 10] 5,491,500 2016年1月1日 0.75‰ 官方人口時鐘
115 新加坡 5,488,500 2016年1月1日 0.75‰ 人口时钟预测
116 斯洛伐克 5,432,900 2016年1月1日 0.74‰ 人口时钟预测
117 土库曼斯坦 5,412,600 2016年1月1日 0.74‰ 人口时钟预测
118 挪威[註 11] 5,085,500 2016年1月7日 0.69‰ 人口时钟预测
119 哥斯达黎加 5,061,900 2016年1月1日 0.69‰ 人口时钟预测
120 中非 4,926,400 2016年1月1日 0.67‰ 人口时钟预测
121 刚果(布) 4,850,500 2016年1月1日 0.66‰ 人口时钟预测
122 愛爾蘭 4,842,100 2016年1月1日 0.66‰ 人口时钟预测
123 利比里亚 4,749,100 2016年1月1日 0.65‰ 人口时钟预测
124 巴勒斯坦[註 12] 4,705,200 2016年1月1日 0.64‰ 人口时钟预测
125 新西蘭 4,648,500 2016年1月1日 0.63‰ 官方人口時鐘
126 黎巴嫩 4,468,300 2016年1月1日 0.61‰ 人口时钟预测
127 格鲁吉亚[註 13] 4,432,500 2016年1月1日 0.61‰ 人口时钟预测
128 克罗地亚 4,254,600 2016年1月1日 0.58‰ 人口时钟预测
129 毛里塔尼亚 4,165,800 2016年1月1日 0.57‰ 人口时钟预测
130 巴拿马 4,025,600 2016年1月1日 0.55‰ 人口时钟预测
131 波赫 3,835,300 2016年1月1日 0.52‰ 人口时钟预测
132 波多黎各 3,704,500 2016年1月1日 0.51‰ 人口时钟预测
133 阿曼 3,590,500 2016年1月1日 0.49‰ 人口时钟预测
134 摩尔多瓦[註 14] 3,549,400 2016年1月1日 0.48‰ 人口时钟预测
135 科威特 3,533,300 2016年1月1日 0.48‰ 人口时钟预测
136 乌拉圭 3,426,800 2016年1月1日 0.47‰ 人口时钟预测
137 阿尔巴尼亚 3,195,900 2016年1月1日 0.44‰ 人口时钟预测
138 蒙古 3,060,100 2016年1月1日 0.42‰ 官方人口時鐘
139 亞美尼亞 2,976,600 2016年1月1日 0.41‰ 人口时钟预测
140 立陶宛 2,954,900 2016年1月1日 0.4‰ 人口时钟预测
141 牙买加 2,788,000 2016年1月1日 0.38‰ 人口时钟预测
142 纳米比亚 2,339,400 2016年1月1日 0.32‰ 人口时钟预测
143 博茨瓦纳 2,140,100 2016年1月1日 0.29‰ 人口时钟预测
144 馬其頓 2,126,600 2016年1月1日 0.29‰ 人口时钟预测
145 卡塔尔 2,117,900 2016年1月1日 0.29‰ 人口时钟预测
146 賴索托 2,078,900 2016年1月1日 0.28‰ 人口时钟预测
147 斯洛維尼亞 2,043,700 2016年1月1日 0.28‰ 人口时钟预测
148 拉脫維亞 1,986,100 2016年1月1日 0.27‰ 人口时钟预测
149 冈比亚 1,969,300 2016年1月1日 0.27‰ 人口时钟预测
150 科索沃 1,852,400 2016年1月1日 0.25‰ 人口时钟预测
151 几内亚比绍 1,799,900 2016年1月1日 0.25‰ 人口时钟预测
152 加彭 1,767,200 2016年1月1日 0.24‰ 人口时钟预测
153 巴林 1,472,600 2016年1月1日 0.2‰ 人口时钟预测
154 千里達及托巴哥 1,332,700 2016年1月1日 0.182‰ 人口时钟预测
155 模里西斯 1,329,600 2016年1月1日 0.182‰ 人口时钟预测
156 东帝汶 1,309,100 2016年1月1日 0.179‰ 人口时钟预测
157 爱沙尼亚 1,303,600 2016年1月1日 0.178‰ 人口时钟预测
158 斯威士兰 1,291,300 2016年1月1日 0.176‰ 人口时钟预测
159 賽普勒斯[註 15] 1,203,900 2016年1月1日 0.164‰ 人口时钟预测
160 吉布提 939,250 2016年1月1日 0.128‰ 人口时钟预测
161 斐济 902,980 2016年1月1日 0.123‰ 人口时钟预测
162 赤道几内亚 817,240 2016年1月1日 0.112‰ 人口时钟预测
163 科摩罗 798,120 2016年1月1日 0.109‰ 人口时钟预测
164 圭亚那 781,450 2016年1月1日 0.107‰ 人口时钟预测
165 不丹 778,130 2016年1月1日 0.106‰ 人口时钟预测
166 西撒拉威 638,760 2016年1月1日 0.087‰ 人口时钟预测
167 蒙特內哥羅 604,250 2016年1月1日 0.083‰ 人口时钟预测
168 所罗门群岛 600,090 2016年1月1日 0.082‰ 人口时钟预测
169 澳門 576,660 2016年1月1日 0.079‰ 人口时钟预测
170 苏里南 558,200 2016年1月1日 0.076‰ 人口时钟预测
171 卢森堡 555,700 2016年1月1日 0.076‰ 人口时钟预测
172 佛得角 523,670 2016年1月1日 0.072‰ 人口时钟预测
173 文莱 441,220 2016年1月1日 0.06‰ 人口时钟预测
174 馬爾他 425,800 2016年1月1日 0.058‰ 人口时钟预测
175 巴哈马 385,880 2016年1月1日 0.053‰ 人口时钟预测
176 伯利兹 351,560 2016年1月1日 0.048‰ 人口时钟预测
177 馬爾地夫 336,410 2016年1月1日 0.046‰ 人口时钟预测
178 冰島 329,610 2016年1月1日 0.045‰ 人口时钟预测
179 巴巴多斯 287,390 2016年1月1日 0.039‰ 人口时钟预测
180 瓦努阿圖 260,820 2016年1月1日 0.036‰ 人口时钟预测
181 聖多美和普林西比 204,030 2016年1月1日 0.028‰ 人口时钟预测
182 萨摩亚 193,470 2016年1月1日 0.026‰ 人口时钟预测
183 圣卢西亚 183,680 2016年1月1日 0.025‰ 人口时钟预测
184 關島 171,540 2016年1月1日 0.023‰ 人口时钟预测
185 库拉索 154,840 2014年1月1日 0.021‰ Official estimate
186 阿鲁巴 108,400 2016年1月1日 0.015‰ 人口时钟预测
187 聖文森及格瑞那丁 107,940 2016年1月1日 0.015‰ 人口时钟预测
188 格瑞那達 107,830 2016年1月1日 0.015‰ 人口时钟预测
189 汤加 105,970 2016年1月1日 0.014‰ 人口时钟预测
190 基里巴斯 105,920 2016年1月1日 0.014‰ 人口时钟预测
191 美屬維京群島 104,930 2016年1月1日 0.014‰ 人口时钟预测
192 密克羅尼西亞聯邦 102,100 2016年1月1日 0.014‰ 人口时钟预测
193 澤西 100,800 2014年 0.014‰ 泽西岛官方数据
194 安地卡及巴布達 93,758 2016年1月1日 0.013‰ 人口时钟预测
195 塞舌尔 91,689 2016年1月1日 0.013‰ 人口时钟预测
196 马恩岛 88,471 2016年1月1日 0.012‰ 人口时钟预测
197 安道尔 79,403 2016年1月1日 0.011‰ 人口时钟预测
198 多米尼克 72,297 2016年1月1日 0.0099‰ 人口时钟预测
199 百慕大 66,364 2016年1月1日 0.0091‰ 人口时钟预测
200 开曼群岛 63,021 2016年1月1日 0.0086‰ 人口时钟预测
201 根西 62,950 2014年 0.0086‰ 根西岛官方数据
202 美属萨摩亚 57,850 2016年1月1日 0.0079‰ 人口时钟预测
203 格陵兰 56,922 2016年1月1日 0.0078‰ 人口时钟预测
204 马绍尔群岛 56,787 2016年1月1日 0.0078‰ 人口时钟预测
205 圣基茨和尼维斯 55,368 2016年1月1日 0.0076‰ 人口时钟预测
206 法罗群岛 50,361 2016年1月1日 0.0069‰ 人口时钟预测
207 北馬里亞納群島 45,259 2016年1月1日 0.0062‰ 人口时钟预测
208 荷屬聖馬丁 38,429 2014年 0.0052‰ Official estimate
209 列支敦斯登 37,623 2016年1月1日 0.0051‰ 人口时钟预测
210 摩納哥 37,393 2016年1月1日 0.0051‰ 人口时钟预测
211 特克斯和凯科斯群岛 37,191 2016年1月1日 0.0051‰ 人口时钟预测
212 圣马力诺 32,572 2016年1月1日 0.0044‰ 人口时钟预测
213 直布罗陀 29,855 2016年1月1日 0.0041‰ 人口时钟预测
214 英屬維爾京群島 27,674 2016年1月1日 0.0038‰ 人口时钟预测
215 帛琉 21,057 2016年1月1日 0.0029‰ 人口时钟预测
216 安圭拉 16,807 2016年1月1日 0.0023‰ 人口时钟预测
217 庫克群島 13,238 2016年1月1日 0.0018‰ 人口时钟预测
218 瑙鲁 10,500 2016年1月1日 0.0014‰ 人口时钟预测
219 图瓦卢 10,140 2016年1月1日 0.0014‰ 人口时钟预测
220 蒙特塞拉特 5,144 2016年1月1日 0.0007‰ 人口时钟预测
221 聖赫勒拿 4,131 2016年1月1日 0.0006‰ 人口时钟预测
222 福克蘭群島[註 16] 3,000 2015年7月1日 0.0004‰ IMF estimate for 2014
223 纽埃 1,613 2011年9月10日 0.0002‰ UN estimate for 2010
224 托克勞 1,411 2011年8月18日 0.0002‰ UN estimate for 2010
225 梵蒂冈 842 2014年7月 0.0001‰ Official estimate
226 皮特凯恩群岛 56 2014年 0‰ UN estimate

Example: Yahoo Stock - (待改寫)

Example: 公開資訊觀測站

Parse XML table

Parse XML table with XML::xmlToDataFrame()

Example

Parse JSON format

Yun can parse JSON with jsonlite in R

library(jsonlite)
library(magrittr)
res <- GET("http://ecshweb.pchome.com.tw/search/v3.3/all/results?q=sony&page=1&sort=rnk/dc")
res_df = content(res, as = "text") %>% 
    fromJSON() %>% 
    .$prods     # equivelent to (function(x) {x$prods})
#> No encoding supplied: defaulting to UTF-8.
str(res_df)
#> 'data.frame':    20 obs. of  11 variables:
#>  $ Id         : chr  "DYAI49-A9006HMLV" "DYAD2O-A9006JDQE" "DYAD2J-A9006I085" "DYAD2R-A9006XKSI" ...
#>  $ cateId     : chr  "DYAI49" "DYAD2O" "DYAD2J" "DYAD2R" ...
#>  $ picS       : chr  "/pic/v1/data/item/201509/D/Y/A/I/4/9/sDYAI49-A9006HMLV000_55f2883d1440e.jpg" "/pic/v1/data/item/201510/D/Y/A/D/2/O/sDYAD2O-A9006JDQE000_560e037b08db2.jpg" "/pic/v1/data/item/201509/D/Y/A/D/2/J/sDYAD2J-A9006I085000_55f7f34abd5c9.jpg" "/pic/v1/data/item/201604/D/Y/A/D/2/R/sDYAD2R-A9006XKSI000_56fe3f0b20c2e.jpg" ...
#>  $ picB       : chr  "/pic/v1/data/item/201605/D/Y/A/I/4/9/DYAI49-A9006HMLV000_57353014df35e.jpg" "/pic/v1/data/item/201604/D/Y/A/D/2/O/DYAD2O-A9006JDQE000_57198b2dc111b.jpg" "/pic/v1/data/item/201509/D/Y/A/D/2/J/DYAD2J-A9006I085000_55f7f34aba324.jpg" "/pic/v1/data/item/201605/D/Y/A/D/2/R/DYAD2R-A9006XKSI000_572ff4b5655b1.jpg" ...
#>  $ name       : chr  "SONY SmartBand 2 智慧心率監測手環_ SWR12" "SONY Xperia Z5" "Sony Xperia C5 Ultra E5553 6吋八核自拍機 " "SONY Xperia Z5 Premium" ...
#>  $ describe   : chr  "▼每日強檔‧瘋殺特賣▼SONY SmartBand 2 智慧心率監測手環_ SWR12" "超值▼送32G卡+保護套+保護貼▼SONY Xperia Z5 5.2吋美型防水旗艦機" "送保護套+玻璃保貼Sony Xperia C5 Ultra E5553 6吋八核自拍機 " "▼送32G卡+手機立架+玻璃貼SONY Xperia Z5 Premium" ...
#>  $ price      : int  3180 16900 8890 19900 11900 16777 20900 4680 2240 13490 ...
#>  $ author     : chr  "" "" "" "" ...
#>  $ brand      : chr  "" "" "" "" ...
#>  $ publishDate: chr  "" "" "" "" ...
#>  $ isPick     : int  0 0 0 0 0 0 0 0 0 0 ...
res_list = content(res, as = "parsed")
str(res_list$prods[[1]])

Parse json with loop

res_df2 = data.frame()
for (i in 1:length(res_list$prods)) {
  res_df2 = rbind(res_df2, 
                  data.frame(res_list$prods[[i]], 
                             stringsAsFactors = FALSE))
}
identical(res_df, res_df2)

Useful R functions to combine list of dataframes

Parse Parse list of data.frames with do.call + rbind

res_df3 = data.frame(do.call(rbind, res_list$prods))
identical(res_df, res_df3)

# An ugly data.frame
str(res_df3)

非結構化資料解析: Regular Expression

Package: stringr - str_replace(), str_replace_all() - str_extract(), str_extract_all()

library(stringr)
fruits <- c("one apple", "two pears", "three bananas")
str_replace(fruits, "[aeiou]", "-")
#> [1] "-ne apple"     "tw- pears"     "thr-e bananas"
str_replace_all(fruits, "[aeiou]", "-")
#> [1] "-n- -ppl-"     "tw- p--rs"     "thr-- b-n-n-s"

shopping_list <- c("apples x4", "bag of flour", "bag of sugar", "milk x2")
str_extract(shopping_list, "[a-z]+")
#> [1] "apples" "bag"    "bag"    "milk"
str_extract_all(shopping_list, "[a-z]+")
#> [[1]]
#> [1] "apples" "x"     
#> 
#> [[2]]
#> [1] "bag"   "of"    "flour"
#> 
#> [[3]]
#> [1] "bag"   "of"    "sugar"
#> 
#> [[4]]
#> [1] "milk" "x"

Exercise : 東森房屋

Exercise: 紫外線即時監測資料

行政院環境保護署環境資源資料開放平台提供了一系列的RESTful Api供大家取用,請試著把 紫外線即時監測資料的資料取回來並轉成data.frame

Answer

紫外線即時監測資料



Save your data

Save your data

write.csv

library(jsonlite)
library(httr)
url = "http://ecshweb.pchome.com.tw/search/v3.3/all/results?q=sony&page=1&sort=rnk/dc"
res_df = GET(url) %>% 
    content(res, as = "text") %>% 
    fromJSON() %>% 
    .$prods     # equivelent to (function(x) {x$prods})
write.csv(res_df, "resources/data/pchome.csv", row.names = FALSE)

download.file

To download a file from the Internet. download.file takes advantage of internet utilities such as curl or wget and may fail if you don’t have any of these utilities in your system.

dest_dir = "resources/data/download"
dir.create(dest_dir, showWarnings = FALSE, recursive = TRUE)

# Download whole HTML file
download.file("https://www.r-project.org/", 
              file.path(dest_dir, "r-project.org.html"))

# Download image
download.file("https://www.r-project.org/Rlogo.png",
              file.path(dest_dir, "Rlogo.png"))

list.files(dest_dir)

writeBin

To write binary data to your local disk.

r = GET("http://opendata.epa.gov.tw/webapi/api/rest/datastore/355000000I-000004/?format=json")
# Set as = "raw" to prevent any character encoding
bin = content(r, as = "raw")
writeBin(bin, "resources/data/download/uv.json")

Database

Case study

References